﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Text;

namespace Windows8Accelerator.Core.Xaml.Helpers
{
    [Flags]
    public enum MergeMode
    { 
        Add = 1,
        Update = 2,
        Remove = 4,
        Replace = 8,
        AddRemove = 5,
        AddUpdate = 3,
        RemoveUpdate = 6,
        AddRemoveUpdate = 7,
        AddReplace = 9,
        RemoveReplace = 12,
        AddRemoveReplace = 13,
    }

    public static class ListHelper
    {
        public static void BindList<T>(this IList<T> baseList, ObservableCollection<T> observableCollection, Func<T, object> idSelector, MergeMode mergeMode = MergeMode.AddRemoveReplace, Action<T, T> updateAction = null)
        {
            observableCollection.CollectionChanged += (sender, e) =>
            {
                switch (e.Action)
                {
                    case NotifyCollectionChangedAction.Add:
                        if ((mergeMode & MergeMode.Add) == MergeMode.Add)
                            MergeLists(baseList, e.NewItems.Cast<T>(), idSelector, MergeMode.Add, updateAction);
                        break;
                    case NotifyCollectionChangedAction.Remove:
                        if ((mergeMode & MergeMode.Remove) == MergeMode.Remove)
                            MergeLists(baseList, e.OldItems.Cast<T>(), idSelector, MergeMode.Remove, updateAction);
                        break;
                    //case NotifyCollectionChangedAction.Move:
                    case NotifyCollectionChangedAction.Replace:
                        try
                        {
                            baseList.Remove((T)e.OldItems[0]);
                            baseList.Insert(e.NewStartingIndex, ((T)e.NewItems[0]));
                        }
                        catch { }
                        break;
                    case NotifyCollectionChangedAction.Reset:
                        baseList.Clear();
                        MergeLists(baseList, observableCollection, idSelector, MergeMode.Add, updateAction);
                        break;
                }
            };
            MergeLists(baseList, observableCollection, idSelector, mergeMode, updateAction);
        }

        public static void MergeLists<T>(this IList<T> baseList, IEnumerable<T> list, Func<T, object> idSelector, MergeMode mergeMode = MergeMode.AddRemoveReplace, Action<T, T> updateAction = null)
        {
            if (list == null)
                return;

            lock (baseList)
            {
                if ((mergeMode & MergeMode.Add) == MergeMode.Add)
                {
                    foreach (var item in list)
                    {
                        if (!baseList.Any((i) => object.Equals(idSelector(i),idSelector(item))))
                            baseList.Add(item);
                    }
                }
                if ((mergeMode & MergeMode.Update) == MergeMode.Update && updateAction != null)
                {
                    foreach (var item in list)
                    {
                        var existingItem = baseList.FirstOrDefault((i) => object.Equals(idSelector(i), idSelector(item)));
                        if (existingItem != null)
                        {
                            updateAction(existingItem, item);
                        }
                    }
                }
                if ((mergeMode & MergeMode.Replace) == MergeMode.Replace)
                {
                    foreach (var item in list)
                    {
                        var existingItem = baseList.FirstOrDefault((i) => object.Equals(idSelector(i),idSelector(item)));
                        if (existingItem != null)
                        {
                            var index = baseList.IndexOf(existingItem);
                            baseList.Insert(index, item);
                            baseList.Remove(existingItem);
                        }
                    }
                }
                if ((mergeMode & MergeMode.Remove) == MergeMode.Remove)
                {
                    foreach (var item in baseList.ToList())
                    {
                        if (!list.Any((i) => object.Equals(idSelector(i),idSelector(item))))
                            baseList.Remove(item);
                    }
                }
            }
        }
    }
}
